package org.pizza.dlock.distributed.lock.advisor;

import org.pizza.dlock.distributed.lock.interceptor.AnnotationDistributedLockInterceptor;
import org.pizza.dlock.distributed.lock.api.DLock;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author 高巍
 * @since 2019-11-27 10:42 上午
 */
public class DistributedLockAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {

    private Advice advice;
    private Pointcut pointcut;

    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }

    @Override
    public Advice getAdvice() {
        return this.advice;
    }

    private Advice buildAdvice(BeanFactory beanFactory) {
        return new AnnotationDistributedLockInterceptor(beanFactory);
    }

    private Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
        ComposablePointcut result = null;
        AnnotationMatchingPointcut mpc;
        for (Iterator iterator = asyncAnnotationTypes.iterator(); iterator.hasNext(); result = result.union(mpc)) {
            Class asyncAnnotationType = (Class) iterator.next();
            Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
            mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
            if (result == null) {
                result = new ComposablePointcut(cpc);
            } else {
                result.union(cpc);
            }
        }
        return result != null ? result : Pointcut.TRUE;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        LinkedHashSet lockAnnotationTypes = new LinkedHashSet(2);
        lockAnnotationTypes.add(DLock.class);
        this.advice = this.buildAdvice(beanFactory);
        this.pointcut = this.buildPointcut(lockAnnotationTypes);
    }
}
